﻿namespace ScrumLite.WebUx.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Web.Mvc;
    using ScrumLite.Services;
    using ScrumLite.Services.Contracts;
    using ScrumLite.WebUx.Models;
    using GoogleChartSharp;
    using GoogleChartSharp.CustomCharts;
    using ScrumLite.Gateways;
    using System.Globalization;
    using System.Configuration;
    using System.Web.Routing;
    
    public class RequestsController : ContextBaseController
    {
        IRequestsService requestsService;

        private static RequestStatus[] statusToUpdateCommitted = new RequestStatus[3] { 
                RequestStatus.Blocked, 
                RequestStatus.Completed, 
                RequestStatus.Postponed };

        public RequestsController()
            : this(new RequestsServiceClient(), new ProjectsServiceClient(), new ProfileServiceClient())
        {

        }

        public RequestsController(IRequestsService requestsService, IProjectsService projectsService, IProfileService profileService)
            : base(profileService, projectsService)
        {
            this.requestsService = requestsService;
        }

        protected override string Decoder(string value)
        {
            if (String.IsNullOrEmpty(value))
            {
                return value;
            }
            else
            {
                return Uri.EscapeUriString(base.Decoder(value));
            }
        }

        public void Read(string projectId, string mode, string workArea)
        {
            int gridpage = 0;

            if (HttpContext != null && HttpContext.Session != null && HttpContext.Session["BacklogGridPage"] != null && HttpContext.Session["BacklogProjectUri"] != null && HttpContext.Session["BacklogProjectUri"].Equals(projectId))
            {
                gridpage = (int) HttpContext.Session["BacklogGridPage"];
            }

            if (HttpContext != null && HttpContext.Session != null && HttpContext.Session["BacklogGridPage"] != null)
            {
                HttpContext.Session.Remove("BacklogGridPage");
            }

            if (HttpContext != null && HttpContext.Session != null && HttpContext.Session["BacklogProjectUri"] != null)
            {
                HttpContext.Session.Remove("BacklogProjectUri");
            }

            string viewToRender;
            string viewTitle;
            switch (mode.ToUpperInvariant())
            {
                case "ALL": 
                    viewToRender = "ReadAll";
                    viewTitle = "Backlog";
                    break;
                case "OPENED": 
                    viewToRender = "ReadOpened";
                    viewTitle = "Iteration Planning";
                    break;
                case "ITERATION": 
                    viewToRender = "ReadCommitted";
                    viewTitle = "Sprint Backlog";
                    break;
                default:
                    throw new ArgumentException("Unknow mode:"+mode);
            }

            RequestsReadViewData data = new RequestsReadViewData()
            {
                
                ProjectName = Projects.GetProjectName(projectId),
                ProjectId = projectId,
                Mode = mode,
                BacklogView = viewToRender,
                WorkAreaName = this.Decoder(workArea),
                ViewTitle = viewTitle,
                GridPage = gridpage
            };

            RenderView("Read", data);
        }

        public void ReadOne(string projectId, int moniker, int gridPage)
        {
            if (HttpContext != null && HttpContext.Session != null)
            {
                HttpContext.Session["BacklogGridPage"] = gridPage;
                HttpContext.Session["BacklogProjectUri"] = projectId;
            }

            renderWrite(projectId, moniker);
        }

        private void renderWrite(string projectId, int? moniker)
        {
            Dictionary<string, string> status = requestsService.GetStatusList();
            string projectName = Projects.GetProjectName(projectId);

            WorkAreasPickerViewData workAreasPickerVD;

            if (moniker != null)
            {
                workAreasPickerVD = new WorkAreasPickerViewData(
                    new Collection<WorkArea>(this.requestsService.GetWorkAreas(projectId)),
                    new Collection<WorkArea>(requestsService.GetWorkAreasByMoniker(projectId, (int)moniker)));
            }
            else
            {
                workAreasPickerVD = new WorkAreasPickerViewData(
                    new Collection<WorkArea>(this.requestsService.GetWorkAreas(projectId)));
            }
            workAreasPickerVD.Title = "Choose workAreas for the request.";
            workAreasPickerVD.CheckBoxesName = "chkWorkAreas";
            workAreasPickerVD.DisplayMode = WorkAreaDisplayModeType.Vertical;
            workAreasPickerVD.ShowFilterButton = false;

            RequestsWriteViewData data = new RequestsWriteViewData(status)
            {
                ProjectName = projectName,
                ProjectId = projectId,
                WorkAreasPickerViewData = workAreasPickerVD
            };

            if (moniker != null)
            {
                Request request = requestsService.GetRequest(projectId, (int)moniker);
                data.Request = request;
            }

            data.ViewTitle = "Add or Edit Request";

            RenderView("Write", data);
        }

        public void Delete(string projectId, string requestId, int gridPage)
        {
            if (HttpContext != null && HttpContext.Session != null)
            {
                HttpContext.Session["BacklogGridPage"] = gridPage;
                HttpContext.Session["BacklogProjectUri"] = projectId;
            }

            requestsService.DeleteRequest(new Guid(requestId));
            RedirectToAction(new { Action = "Read", Mode = "All" });
        }

        public void New(string projectId, int gridPage)
        {
            if (HttpContext != null && HttpContext.Session != null)
            {
                HttpContext.Session["BacklogGridPage"] = gridPage;
                HttpContext.Session["BacklogProjectUri"] = projectId;
            }

            renderWrite(projectId, null);
        }

        public void Write(string projectId, int moniker, string id, string requestor, string title, int? priority, int? weight, string notes, byte status, bool? committed, string mode, DateTime? dueOn)
        {
            char separator = ',';

            if (Request.Form["chkWorkAreas"] == null)
            {
                Dictionary<string, string> statusList = requestsService.GetStatusList();

                WorkAreasPickerViewData workAreasPickerVD = new WorkAreasPickerViewData(new Collection<WorkArea>(this.requestsService.GetWorkAreas(projectId)))
                {
                    Title = "Choose workAreas for the request.",
                    CheckBoxesName = "chkWorkAreas",
                    DisplayMode = WorkAreaDisplayModeType.Vertical,
                    ShowFilterButton = false
                };

                RequestsWriteViewData data = new RequestsWriteViewData(statusList)
                {
                    ProjectName = Projects.GetProjectName(projectId),
                    WorkAreasPickerViewData = workAreasPickerVD
                };

                Request request;
                if (!String.IsNullOrEmpty(id))
                {
                    request = requestsService.GetRequest(projectId, (int)moniker);
                }
                else
                {
                    request = new Request();
                }

                request.Requestor = requestor;
                request.Title = title;
                request.Priority = priority;
                request.Weight = weight;
                request.Notes = notes;
                request.Status = (RequestStatus)status;
                request.Committed = committed.HasValue ? committed.Value : false;
                request.DueOn = dueOn;

                data.Request = request;
                data.Message = "You must select one or more workAreas for the request.";

                RenderView("Write", data);

                return;
            }

            string[] selectedWorkAreas = Request.Form["chkWorkAreas"].Split(separator);
            List<WorkArea> listWorkAreas = new List<WorkArea>();

            foreach (string item in selectedWorkAreas)
            {
                listWorkAreas.Add(requestsService.GetWorkArea(projectId, new Guid(item)));
            }

            Request requestToSave = new Request()
            {
                Notes = notes,
                Priority = priority,
                Status = (RequestStatus)status,
                Title = title,
                Weight = weight,
                Committed = committed.HasValue ? committed.Value : false,
                Requestor = requestor,
                DueOn = dueOn,
                WorkAreas = listWorkAreas.ToArray()
            };

            if (string.IsNullOrEmpty(id))
            {
                requestToSave.Id = Guid.NewGuid();
                int monikerReturned = requestsService.AddRequest(projectId, requestToSave);
                requestToSave.Moniker = monikerReturned;
            }
            else
            {
                requestToSave.Moniker = moniker;
                requestToSave.Id = new Guid(id);

                requestsService.UpdateRequest(projectId, requestToSave);
            }

            switch (mode)
            {
                case "close":
                    RedirectToAction(new { Action = "Read", Mode = "All" });
                    break;
                case "new":
                    renderWrite(projectId, null);
                    break;
                case "update":
                    renderWrite(projectId, requestToSave.Moniker);
                    break;
                default:
                    throw new ArgumentException("Unknow mode:"+mode);
            }
        }

        public void ReadWorkArea(string projectId, int moniker)
        {
            WorkArea[] projectWorkAreas = requestsService.GetWorkAreas(projectId);
            WorkArea[] currentWorkAreas = requestsService.GetWorkAreasByMoniker(projectId, moniker);

            ReadWorkAreaViewData data = new ReadWorkAreaViewData(new Collection<WorkArea>( projectWorkAreas ),new Collection<WorkArea>( currentWorkAreas ))
            {
                ProjectId = projectId,
                ProjectName = Projects.GetProjectName(projectId),
                ViewTitle = "Assign Work Area to Request"
            };

            RenderView("ReadWorkArea", data);
        }

        public void Classify(string projectId, int moniker)
        {
            char separator = ',';
            string[] selectedWorkAreas = null;
            if (Request.Form.AllKeys.Contains("chkWorkArea"))
            {
                selectedWorkAreas = Request.Form["chkWorkArea"].Split(separator);
            }

            requestsService.AssignWorkAreas(projectId, moniker, selectedWorkAreas);

            RedirectToAction(new { Action = "Read", Mode = "All" });
        }

        public void CleanCommitted(string projectId)
        {
            requestsService.CleanCommitted(projectId, statusToUpdateCommitted);
            RedirectToAction(new { Action = "Read", Mode = "Opened" });
        }

        public void RequestsMetrics(string projectId)
        {
            Dictionary<WorkArea, Dictionary<RequestStatus,int>> requestsByStatusByWorkAreas = requestsService.GetRequestsByStatusByWorkArea(projectId);
            Collection<string> gChartRequestsByStatusByWorkAreaBars = new Collection<string>(CreateRequestsByStatusByWorkAreaBarsUrl(requestsByStatusByWorkAreas));

            RequestsMetricsData data = new RequestsMetricsData(
                requestsService.GetWeeklyWeight(projectId),
                requestsService.GetRequestsByStatus(projectId),
                requestsByStatusByWorkAreas,
                gChartRequestsByStatusByWorkAreaBars);

            data.GChartRequestsByStatusPie = CreateRequestsByStatusPieUrl(data);
            data.GChartRequestsLine = CreateWeightLineUrl(data);
            data.ProjectId = projectId;
            data.ProjectName = Projects.GetProjectName(projectId);
            data.ViewTitle = "Requests Metrics";
            RenderView("RequestsMetrics", data);
        }

        #region Google Charts helper methods
        private static string CreateRequestsByStatusPieUrl(RequestsMetricsData data)
        {
            if (data.RequestsByStatus.Count() == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyRequestByStatusPie.png");
            }
            else
            {
                LabeledValuePieChart chartBuilder = new LabeledValuePieChart(350, 150, PieChartType.ThreeD);
                chartBuilder.SetTitle("Requests by status");
                NormalizeRequestsByStatusData(data.RequestsByStatus);
                Dictionary<string, float> dataDictionary = data.RequestsByStatus.OrderBy(k => k.Key).ToDictionary(k => k.Key.ToString(), v => (float)v.Value);
                string[] labels = dataDictionary.Keys.ToArray();
                float[] pieData = dataDictionary.Values.ToArray();

                chartBuilder.SetDataSetColors(new string[] { "FFFF55", "AAAAAA", "FF5555", "55FF55", "5555FF" });
                chartBuilder.SetPieChartLabels(labels);
                chartBuilder.SetData(pieData);

                return chartBuilder.GetUrl();
            }
        }

        private static string CreateWeightLineUrl(RequestsMetricsData data)
        {
            if (data.WeeklyWeight.Count() == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyRequestByStatusPie.png");
            }
            else
            {
                LineChart chartBuilder = new LineChart(500, 250, LineChartType.SingleDataSet);
                chartBuilder.AddLineStyle(new LineStyle(3, 1, 0));
                chartBuilder.AddLineStyle(new LineStyle(1, 6, 3));
                chartBuilder.SetTitle("Weekly weight completed");
                chartBuilder.SetLegend(new string[] { "Weight", "Average" });
                chartBuilder.SetDataSetColors(new string[] { "55FF55", "0000FF" });

                List<float> chartData = new List<float>();
                List<string> weekLabels = new List<string>();
                foreach (KeyValuePair<int, int> week in data.WeeklyWeight)
                {
                    chartData.Add(week.Value);
                    weekLabels.Add(string.Format(CultureInfo.InvariantCulture, "W{0}", week.Key));
                }

                ChartAxis weeklyAxis;
                if (weekLabels.Count > 0)
                {
                    weeklyAxis = new ChartAxis(ChartAxisType.Bottom, weekLabels.ToArray());
                }
                else
                {
                    weeklyAxis = new ChartAxis(ChartAxisType.Bottom, new string[] { "." });
                }
                chartBuilder.AddAxis(weeklyAxis);

                float maxValueRange = 0;
                if (chartData.Count > 0)
                {
                    maxValueRange = chartData.Max();
                }
                int maxValueLabel = (Convert.ToInt32(maxValueRange / 10) + 1) * 10;
                ChartAxis unitAxis = new ChartAxis(ChartAxisType.Left);
                unitAxis.SetRange(0, maxValueLabel);
                chartBuilder.AddAxis(unitAxis);

                double? average = 0;
                if (data.WeeklyWeight.Count > 0)
                {
                    average = data.WeeklyWeight.Average(w => w.Value);
                }
                List<float[]> allData = new List<float[]>();
                allData.Add(chartData.ToArray());
                allData.Add(new float[] { (float)average, (float)average });
                chartBuilder.SetData(allData, maxValueLabel);

                return chartBuilder.GetUrl();
            }
        }

        private static string[] CreateRequestsByStatusByWorkAreaBarsUrl(Dictionary<WorkArea, Dictionary<RequestStatus, int>> requestsByStatusByWorkAreas)
        {
            List<string> requestsByStatusUrls = new List<string>();
            foreach (KeyValuePair<WorkArea, Dictionary<RequestStatus, int>> oneWorkArea in requestsByStatusByWorkAreas)
            {
                requestsByStatusUrls.Add(CreateRequestsByStatusBarsUrl(oneWorkArea.Key.Name, oneWorkArea.Value));
            }
            return requestsByStatusUrls.ToArray();
        }

        private static string CreateRequestsByStatusBarsUrl(string workAreaName, Dictionary<RequestStatus, int> requestsByStatus)
        {
            if (requestsByStatus.Count() == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyRequestByStatusPie.png");
            }
            else
            {
                BarChart chartBuilder = new BarChart(400, 150, BarChartOrientation.Vertical, BarChartStyle.Grouped);
                chartBuilder.SetTitle(workAreaName);
                chartBuilder.SetBarWidth(20, 5, 50);
                string[] allStatus = Enum.GetNames(typeof(RequestStatus));
                chartBuilder.SetLegend(allStatus);
                chartBuilder.SetDataSetColors(new string[] { "FFFF55", "AAAAAA", "FF5555", "55FF55", "5555FF" });


                NormalizeRequestsByStatusData(requestsByStatus);

                List<float[]> chartData = new List<float[]>();
                float maxValueRange = 0;
                foreach (string status in allStatus)
                {
                    RequestStatus requestStatus = (RequestStatus)Enum.Parse(typeof(RequestStatus), status);
                    chartData.Add(new float[] { requestsByStatus[requestStatus] });
                    maxValueRange = Math.Max(maxValueRange, requestsByStatus[requestStatus]);
                }

                int maxValueLabel = (Convert.ToInt32(maxValueRange / 10) + 1) * 10;
                ChartAxis unitAxis = new ChartAxis(ChartAxisType.Left);
                unitAxis.SetRange(0, maxValueLabel);
                chartBuilder.AddAxis(unitAxis);

                chartBuilder.SetData(chartData, maxValueLabel);

                return chartBuilder.GetUrl();
            }
        }

        private static void NormalizeRequestsByStatusData(Dictionary<RequestStatus, int> requestsByStatus)
        {
            foreach (RequestStatus status in Enum.GetValues(typeof(RequestStatus)))
            {
                if (!requestsByStatus.ContainsKey(status))
                {
                    requestsByStatus.Add(status, 0);
                }
            }
        }
        #endregion
    }
}
